/********************************************************************************
 * @file    bsp_adc.c
 * @author  jianqiang.xue
 * @version V1.1.1
 * @date    2023-03-06
 * @brief   采用动态创建方式
 ********************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "RTE_Components.h"
#include CMSIS_device_header

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "bsp_adc.h"
#include "bsp_exti.h"
#include "bsp_gpio.h"

/* Private Includes ----------------------------------------------------------*/
#include "ls_gpio.h"
#include "ls_syscfg.h"

/* Private Define ------------------------------------------------------------*/
#define ADC0_CH_NUM (8)
#define CH_NUM      (LS_ADC0_CH)

#define ADC0_BASE     ADC
#define ADC0_IRQn     ADC_IRQn

#ifndef LS_APP_IO_CHANGE_SUPPORT
#define IO_TYPE(io)  (g_io_cfg[io].type)
#else // 支持可变
#include "app_io.h"
#define IO_TYPE(io)  (g_io_cfg[io].type)
#endif
/* Private Variables ---------------------------------------------------------*/
#if LS_ADC0_EN
ADC_HandleTypeDef *g_adc0_handle_t = NULL;
// ADC采集数据存储buff
uint16_t *g_adc0_val_buf = NULL;
// 定义ADC采集完毕回调函数
typedef void (*bsp_adc0_callback)(void);
static bsp_adc0_callback g_irq_adc0_cb = NULL;

bsp_adc_cfg_t g_adc0_cfg = {.ch_val = 0, .clk_sel = LS_ADC0_CLK_DIV, .sample_num = LS_ADC0_SAMPLING_NUM};
#endif

/* External Variables --------------------------------------------------------*/
/* Public Function Prototypes ------------------------------------------------*/
void ADC_IRQHandler(void) {
    /* USER CODE BEGIN ADC_IRQn 0 */
#if LS_ADC0_EN
    if (g_adc0_handle_t)
        HAL_ADC_IRQHandler(g_adc0_handle_t);
#endif
    /* USER CODE END ADC_IRQn 0 */
}

#if LS_ADC0_EN
/**
 * @brief  将指定引脚复用为ADC引脚（复用模拟输入）
 * @param  io: 引脚号
 */
void io_cfg_as_adc0(uint8_t io) {
    if (io == 0 || (io > LS_IO_NUM - 1)) return;
    if (IO_TYPE(io) < IO_TYPE_ADC0_CH0 && IO_TYPE(io) > IO_TYPE_ADC0_CH6)
        return;
    GPIO_InitTypeDef gpio_init_struct = {
        .Pin             = g_io_cfg[io].io.pin,
        .Mode            = GPIO_MODE_ANALOG,                  // 【模拟输入】
        .OpenDrain       = GPIO_OPENDRAIN,                    // 开漏输出
        .Debounce.Enable = GPIO_DEBOUNCE_DISABLE,             // 禁止输入去抖动
        .SlewRate        = GPIO_SLEW_RATE_HIGH,               // 电压转换速率
        .DrvStrength     = GPIO_DRV_STRENGTH_HIGH,            // 驱动强度
        .Pull            = GPIO_NOPULL,                       // 无上下拉
    };
    SET_BIT(RCC->HCLKEN, g_io_cfg[io].io.periph);
    HAL_GPIO_Init(g_io_cfg[io].io.port, (GPIO_InitTypeDef*)&gpio_init_struct);

    g_adc0_cfg.ch_val |= 1 << (g_io_cfg[io].type - IO_TYPE_ADC0_CH0);
}

/**
 * @brief  ADC0初始化，并使能通道
 * @retval 0--初始化成功  1--结构体创建失败  2--临时数组申请失败  3--通道数量为0
 */
uint8_t bsp_adc0_init(void) {
#if CH_NUM
    if (g_adc0_handle_t) return 0;

    if ((LS_ADC0_CH & 0x80) == 0x80) // 是否启动SYS_VCC检测。（可以直接当电池电压使用）
        g_adc0_cfg.ch_val |= 0x80;
    if (g_adc0_cfg.ch_val == 0) return 3;

    g_adc0_handle_t = malloc(sizeof(ADC_HandleTypeDef)); // 申请空间
    if (!g_adc0_handle_t) return 1; // ADC0结构体无法申请空间

    g_adc0_val_buf = malloc(sizeof(uint16_t) * ADC0_CH_NUM); // 申请空间
    if (!g_adc0_val_buf) {
        free(g_adc0_handle_t);
        return 2; // ADC0临时数据无法申请空间
    }
    memset(g_adc0_val_buf, 0, sizeof(uint16_t) * ADC0_CH_NUM);
    // 结构体赋值
    g_adc0_handle_t->Instance                = ADC0_BASE;
    g_adc0_handle_t->Init.SamplingTime       = ADC_SAMPLE_8CYCLE;          // 采样周期
    g_adc0_handle_t->Init.SingleContinueMode = ADC_MODE_CONTINUE;          // 连续转换模式
    g_adc0_handle_t->Init.AutoAccumulation   = ADC_AUTOACC_DISABLE;        // 禁止ADC转换结果自动累加
    g_adc0_handle_t->Init.CircleMode         = ADC_MULTICHANNEL_NONCIRCLE; // 禁止ADC循环转换模式
    g_adc0_handle_t->Init.ExternalTrigConv1  = ADC_SOFTWARE_START;         // 禁用自动触发ADC转换
    g_adc0_handle_t->Init.ClkSel             = (g_adc0_cfg.clk_sel << 4);
    g_adc0_handle_t->Init.NbrOfConversion    = g_adc0_cfg.sample_num; // 连续转换次数
    // 使能通道
    g_adc0_handle_t->Init.ContinueChannelSel = g_adc0_cfg.ch_val;

    __HAL_RCC_ADC_CLK_ENABLE();
    HAL_ADC_Init(g_adc0_handle_t);

    // ADC comparison settings
    ADC_ThresholdConfTypeDef adc0_threshold_conf_t;
    adc0_threshold_conf_t.ITMode = DISABLE;
    adc0_threshold_conf_t.CompareMode = ADC_COMP_THRESHOLD_NONE;  // 禁止 ADC比较中断控制
    HAL_ADC_ThresholdConfig(g_adc0_handle_t, &adc0_threshold_conf_t);
    HAL_NVIC_SetPriority(ADC0_IRQn, 2);
#endif
    return 0;
}

/**
 * @brief  ADC0功能关闭，并移除
 */
void bsp_adc0_deinit(void) {
#if CH_NUM
    if (!g_adc0_handle_t) return;
    HAL_NVIC_DisableIRQ(ADC0_IRQn);  // 关闭ADC中断
    HAL_ADC_DeInit(g_adc0_handle_t);
    __HAL_RCC_ADC_CLK_DISABLE();
    g_irq_adc0_cb = NULL;

    free(g_adc0_handle_t);
    g_adc0_handle_t = NULL;

    if (g_adc0_val_buf) free(g_adc0_val_buf);
    g_adc0_val_buf = NULL;
#endif
}

/**
 * @brief  ADC0 启动采样功能
 */
void bsp_adc0_start(void) {
#if CH_NUM
    if (!g_adc0_handle_t) return;
    HAL_ADC_Start_IT(g_adc0_handle_t);  // 启动ADC中断转换
    HAL_NVIC_EnableIRQ(ADC0_IRQn);  // 使能ADC中断
#endif
}

/**
 * @brief  ADC0 停止采样功能
 */
void bsp_adc0_stop(void) {
#if CH_NUM
    if (!g_adc0_handle_t) return;
    HAL_NVIC_DisableIRQ(ADC0_IRQn);  // 关闭ADC中断
    HAL_ADC_Stop_IT(g_adc0_handle_t);  // 停止ADC中断转换
#endif
}

/**
 * @brief  得到ADC0采样值
 * @param  ch_num: 通道值
 * @retval 通道对应的ADC值
 */
uint16_t bsp_adc0_get_ch_val(uint8_t ch_num) {
    if (!g_adc0_handle_t) return 0;
    if (ch_num >= ADC0_CH_NUM) return 0xFFFF;
    if (!g_adc0_val_buf) return 0;
    return g_adc0_val_buf[ch_num];
}

/**
 * @brief  注册ADC0采样完毕回调函数
 * @param  *event: 绑定回调事件
 * @retval 0--失败 1--成功
 */
uint8_t bsp_adc0_irq_callback(void* event) {
    if (g_irq_adc0_cb != NULL)
        return false;
    else
        g_irq_adc0_cb = (bsp_adc0_callback)event;
    return true;
}

#else
uint8_t bsp_adc0_init(void) {
    return 0xff;
}

void bsp_adc0_deinit(void) {
}

void bsp_adc0_start(void) {
}

uint16_t bsp_adc0_get_ch_val(uint8_t ch_num) {
    return 0;
}
#endif


void HAL_ADC_MultiChannel0_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
#if LS_ADC0_EN && LS_ADC0_CH
    if (hadc->Instance == ADC0_BASE) {
        if (g_adc0_val_buf)
            g_adc0_val_buf[0] = HAL_ADC_GetValue(hadc, ADC_CONTINUE_CHANNEL_0);
    }
#endif
}

void HAL_ADC_MultiChannel1_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
#if LS_ADC0_EN &&(LS_ADC0_CH & 0x01)
    if (hadc->Instance == ADC0_BASE) {
        if (g_adc0_val_buf)
            g_adc0_val_buf[1] = HAL_ADC_GetValue(hadc, ADC_CONTINUE_CHANNEL_1);
    }
#endif
}

void HAL_ADC_MultiChannel2_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
#if LS_ADC0_EN && (LS_ADC0_CH & 0x02)
    if (hadc->Instance == ADC0_BASE) {
        if (g_adc0_val_buf)
            g_adc0_val_buf[2] = HAL_ADC_GetValue(hadc, ADC_CONTINUE_CHANNEL_2);
    }
#endif
}

void HAL_ADC_MultiChannel3_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
#if LS_ADC0_EN && (LS_ADC0_CH & 0x04)
    if (hadc->Instance == ADC0_BASE) {
        if (g_adc0_val_buf)
            g_adc0_val_buf[3] = HAL_ADC_GetValue(hadc, ADC_CONTINUE_CHANNEL_3);
    }
#endif
}

void HAL_ADC_MultiChannel4_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
#if LS_ADC0_EN && (LS_ADC0_CH & 0x08)
    if (hadc->Instance == ADC0_BASE) {
        if (g_adc0_val_buf)
            g_adc0_val_buf[4] = HAL_ADC_GetValue(hadc, ADC_CONTINUE_CHANNEL_4);
    }
#endif
}

void HAL_ADC_MultiChannel5_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
#if LS_ADC0_EN && (LS_ADC0_CH & (1<<5))
    if (hadc->Instance == ADC0_BASE) {
        if (g_adc0_val_buf)
            g_adc0_val_buf[5] = HAL_ADC_GetValue(hadc, ADC_CONTINUE_CHANNEL_5);
    }
#endif
}

void HAL_ADC_MultiChannel6_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
#if LS_ADC0_EN && (LS_ADC0_CH & (1<<6))
    if (hadc->Instance == ADC0_BASE) {
        if (g_adc0_val_buf)
            g_adc0_val_buf[6] = HAL_ADC_GetValue(hadc, ADC_CONTINUE_CHANNEL_6);
    }
#endif
}

void HAL_ADC_MultiChannel7_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
#if LS_ADC0_EN && (LS_ADC0_CH & (1<<7))
    if (hadc->Instance == ADC0_BASE) {
        if (g_adc0_val_buf)
            g_adc0_val_buf[7] = HAL_ADC_GetValue(hadc, ADC_CONTINUE_CHANNEL_7);
    }
#endif
}

/**
 * @brief  [重定义] ADC采样完毕回调函数
 * @param  hadc: ADC handle
 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
#if LS_ADC0_EN
    if (hadc->Instance == ADC0_BASE) {
        if (g_irq_adc0_cb)
            g_irq_adc0_cb();
    }
#endif
}
